package dziennik.classes;
import dziennik.classes.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;



/**
 * 
 * <p>Klasa Polaczenie umozliwia:</p>
 * <ul>
 * <li>nawiazanie polaczenia ze zdefiniowana wewnatrz klasy baza danych</li>
 * <li>rozlaczenie z baza danych</li>
 * <li>serializacje obiektu do tablicy bajtow</li>
 * <li>deserializacje tablicy bajtow do obiektu typu ArrayList</li>
 * <li>wyslanie zaktualizowanego ucznia do dziennika</li>
 * <li>wyslanie calego zaktualizowanego dziennika do bazy</li>
 * <li>dodawanie nowego ucznia</li>
 * <li>uproszczone wysylanie zapyta� do bazy danych</li>
 * </ul>
 * 
 * @author Elwira Kowalewska
 *
 */
public class Polaczenie
{
	private Boolean polaczono = false;			// flaga mowiaca, czy jest polaczenie z baza, czy nie
	private Connection conn = null;				// uchwyt do polaczenia z baza, potrzebny do tworzenia zapytan i komend SQL
	private Statement st = null;				// uchwyt do polecen SQL
	
	/*
	// konfiguracja do bazy na darmowym serwerze db4free
	String url = "jdbc:mysql://88.198.43.24:3306/";
	String dbName = "a1647325";
    String driver = "com.mysql.jdbc.Driver";                        // sterownik z pliku .jar - trzeba dodac ten plik do 'classpath' w 'run configurations'
    String userName = "a1647325u";
    String password = "d83KQ12f";
	// ---
	*/
	
	// konfiguracja do bazy na http://axsoft.net/phpmyadmin/
	private String url = "jdbc:mysql://axsoft.net:3306/";			// adres bazy
	private String dbName = "hgn_dziennik_opt";							// nazwa bazy na serwerze, w ktorej przechowywane sa dzienniki
	private String driver = "com.mysql.jdbc.Driver";				// sterownik z pliku .jar - trzeba dodac ten plik do 'classpath' w 'run configurations'
	private String userName = "hgn";								// nazwa uzytkownika do bazy
	private String password = "1q2w3e4r";							// haslo dostepu do bazy
	
	private String parametr = "?useUnicode=true&characterEncoding=utf8";//DMA 08.01 dziala utf8 
			//"?useUnicode";			zle
			//"?charSet=UTF_8"; 		 zle
			//"?charSet=UNICODE"; 		zle
	
	// ---

    public Connection getConn() {
        return conn;
    }

    public String getDbName() {
        return dbName;
    }

    public String getDriver() {
        return driver;
    }

    public String getParametr() {
        return parametr;
    }

    public String getPassword() {
        return password;
    }

    public Boolean getPolaczono() {
        return polaczono;
    }

    public Statement getSt() {
        return st;
    }

    public String getUrl() {
        return url;
    }

    public String getUserName() {
        return userName;
    }

    public void setConn(Connection conn) {
        this.conn = conn;
    }

    public void setDbName(String dbName) {
        this.dbName = dbName;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public void setParametr(String parametr) {
        this.parametr = parametr;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setPolaczono(Boolean polaczono) {
        this.polaczono = polaczono;
    }

    public void setSt(Statement st) {
        this.st = st;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
	
	/**
	 * <p>
	 * laczy z baza uzywajac prywatnych parametrow polaczenia, zadeklarowanych
	 * wewnatrz klasy Polaczenie. Parametry te nie moga byc zmienione, gdyz klasa ta
	 * nie udostepnia metod do ich zmiany.
	 * </p> 
	 * 
	 * @return funkcja zwraca zmienna logiczna true/false, ktora mowi o tym, czy polaczenie zostalo
	 * poprawnie zainicjalizowane, czy nie
	 */
	public String polacz_z_baza()
	{
		try
		{
			// ustanowienie polaczenia
			conn = DriverManager.getConnection(url+dbName+parametr, userName, password);
			
			
			// st to obiekt, ktorym poslugujemy sie do przesylania danych pomiedzy baza a programem
			// to taki uchwyt do wywolywania polecen mysql
			st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
			
			polaczono = true;
                        System.out.println("connected 2 database");
			//return "connected";
                        return null;
		}
		// jesli nie udalo sie zainicjalizowac polaczenia
		catch (SQLException e1)
		{
			e1.printStackTrace();
			polaczono = false;
			conn = null;
			st = null;
			//return "not connected";
                        System.out.println("not connected 2 database");
                        return null;
		}
	}
	
	
	
	/**
	 * <p>
	 * rozlacza z baza danych. 
	 * </p> 
	 * 
	 * @return funkcja zwraca zmienna logiczna true/false, ktora mowi o tym,
	 * czy polaczenie zostalo poprawnie zako�czone, czy nie. Jesli polaczenie
	 * nie bylo wczesniej nawiazane, funkcja rowniez zwraca wartosc true.
	 */
	public Boolean rozlacz_z_baza()
	{
		try
		{
			if (polaczono == true)		// bylismy polaczeni poprawnie
			{
				conn.close();
				conn = null;
				st = null;
				polaczono = false;
				return true;
			}
			else
			{
				return true;			// nie bylismy w ogole polaczeni z baza
			}
		}
		catch (SQLException e1)
		{
			e1.printStackTrace();
			return false;				// nie udalo sie rozlaczyc z baza
		}
	}
	
	
	
	/**
	 * <p>
	 * wysyla zaktualizowany obiekt Dziennik do bazy. Funkcja obsluguje tylko aktualizacje
	 * danych, ktore zawarte sa wewnatrz listy uczniow. Nie umozliwia natomiast usuwania uczniow
	 * z bazy, dodawania uczniow, itd.
	 * </p> 
	 * 
	 * @param dz - obiekt klasy Dziennik, ktory zawiera informacje o zapisanych w nim uczniach.
	 * @param rs - zbior wynikow, ktore zwrocilo poprzednie zapytanie SQL. Powinien on zawierac tylko
	 * ten dziennik, ktory ma byc z powrotem wyslany.
	 * 
	 * @return true, jesli wysylanie zostalo przeprowadzone poprawnie; false jesli wystapily
	 * jakies bledy i wyslanie dziennika nalezy powtorzyc
	 */
	public Boolean wyslij_dziennik(Dziennik dz, ResultSet rs)
	{
		Boolean poprawnie = true;
		
		if (polaczono == true && st != null)
		{
			try
			{
				rs.beforeFirst();
		
				// przechodzimy przez wszystkie wiersze wyniku zwroconego przez poprzenie zapytanie i uaktualniamy poszczegolne wiersze
				while (rs.next())
				{
					int id = rs.getInt("id_ucznia");		// pobieramy z aktualnego wiersza z wyniku bazy numer id ucznia
					
					// przejdz przez wszystkie komorki listy Uczen_klasy
					Iterator it = dz.getUczen_klasy().iterator(); 
					while(it.hasNext())
					{
						Uczen_klasy uczkl = (Uczen_klasy) it.next();
						if (uczkl.getId() == id)
						{							
							try
							{
								byte[] przedmioty_byte = serializuj(uczkl.getPrzedmioty());
								rs.updateObject("przedmioty", przedmioty_byte);
								
								byte[] uwagi_byte = serializuj(uczkl.getUwagi());
								rs.updateObject("uwagi", uwagi_byte);
								
								rs.updateRow();
							}
							catch (SQLException e)
							{
								e.printStackTrace();
								poprawnie = false;
							}
						}
					}
				}
				if (poprawnie == false)
					return false;
				else
					return true;
			}
			catch (SQLException e1)
			{
				e1.printStackTrace();
				return false;
			}
		}
		else
			return false;
	}
	
	
	
	/**
	 * <p>wysyla zaktualizowany obiekt Uczen_klasy do bazy. Funkcja obsluguje tylko aktualizacje
	 * danych, ktore zawiera ucze�, nie umozliwia natomiast zmiany danych osobowych ucznia.</p> 
	 *
	 * @param ucz - obiekt klasy Uczen_klasy, ktory zawiera informacje o postepach w nauce danego ucznia.
	 * @param rs - zbior wynikow, ktore zwrocilo poprzednie zapytanie SQL. Nawet jesli rs bedzie przechowywal
	 * dane uczniow z calego dziennika, to wyslany zostanie tylko jeden ucze�, przekazany w parametrze.
	 * 
	 * @return true, jesli wysylanie zostalo przeprowadzone poprawnie; false jesli wystapily jakies bledy
	 * i wyslanie ucznia nalezy powtorzyc.
	 */
	public Boolean wyslij_ucznia(Uczen_klasy ucz, ResultSet rs)
	{
		if (polaczono == true && st != null)
		{
			try
			{
				rs.beforeFirst();
				
				Boolean powtarzaj = true;
				
				while (powtarzaj == true)
				{
					rs.next();
					
					int id = rs.getInt("id_ucznia");		// pobieramy z aktualnego wiersza z wyniku bazy numer id ucznia
					
					if (ucz.getId() == id)
					{
						powtarzaj = false;
						byte[] przedmioty_byte = serializuj(ucz.getPrzedmioty());
						rs.updateObject("przedmioty", przedmioty_byte);
						
						byte[] uwagi_byte = serializuj(ucz.getUwagi());
						rs.updateObject("uwagi", uwagi_byte);
						
						rs.updateRow();
					}
				}
				return true;
			}
			catch (SQLException e1)
			{
				e1.printStackTrace();
				return false;
			}
		}
		else
			return false;
	}
	
	
	
	/**
	 * <p>funkcja dodaje nowego ucznia do tabeli w bazie danych, podanej jako parametr dz</p>
	 * 
	 * @param ucz - nowo utworzony uczen, ktory ma zostac dodany do bazy
	 * @param dz - la�cuch znakow, oznaczajacy do ktorego dziennika dodac nowego ucznia
	 * @return true, jesli dodanie ucznia przebieglo poprawnie; false, jesli nastapil jakis blad.
	 * False moze rowniez oznaczac, ze ucze� o takim id juz istnieje w bazie.
	 */
	public Boolean dodaj_ucznia(Uczen_klasy ucz, String dz)
	{
		Statement st;
		try
		{
			st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
			ResultSet rs = st.executeQuery("select * from " + dz + " limit 0");
			
			rs.moveToInsertRow();
			rs.updateInt("id", ucz.getId());
			rs.updateObject("przedmioty", serializuj(ucz.getPrzedmioty()));
			rs.updateObject("uwagi", serializuj(ucz.getUwagi()));	
			rs.insertRow();
			
			return true;
		}
		catch (SQLException e)
		{
			e.printStackTrace();
			return false;
		}
	}
	
	
	
	/**
	 * <p>serializuje przekazany w parametrze obiekt do tablicy bajtow</p>
	 * @param o - obiekt, ktory ma byc serializowany
	 * @return tablica bajtow, zawierajaca wyserializowany obiekt
	 */
	public static byte[] serializuj(Object o)
	{
		ByteArrayOutputStream baos;
		ObjectOutputStream out;
		
		try
		{
			baos = new ByteArrayOutputStream();
			out = new ObjectOutputStream(baos);	
			out.writeObject(o);
			out.close();
			
			return baos.toByteArray();
		}
		catch (IOException e)
		{
			e.printStackTrace();
			return null;
		}
	}
	
	
	
	/**
	 * deserializacja tablicy bajtow do obiektu typu ArrayList
	 * @param b - tablica bajtow, odczytana z bazy danych
	 * @return obiekt typu ArrayList, jesli odczytany z bazy ciag bajtow jest
	 * po deserializacji tego typu. Zwraca null, jesli nie jest to obiekt typu ArrayList
	 * lub jesli napotkano jakies bledy.
	 */
	public static ArrayList deserializuj(byte[] b)
	{
		try
		{
			ByteArrayInputStream bais = new ByteArrayInputStream (b);
			ObjectInputStream ois = new ObjectInputStream (bais);
			Object o = ois.readObject();
			
			if (o instanceof ArrayList)
			{
				if ((!((ArrayList) o).isEmpty()) && ((ArrayList) o).get(0) instanceof Przedmiot)
				{
					return (ArrayList<Przedmiot>) o;
				}
				else if ((!((ArrayList) o).isEmpty()) && ((ArrayList) o).get(0) instanceof Uwaga)
				{
					return (ArrayList<Uwaga>) o;
				}
				else if ((!((ArrayList) o).isEmpty()) && ((ArrayList) o).get(0) instanceof Uprawnienia)
				{
					return (ArrayList<Uprawnienia>) o;
				}
				else return (ArrayList) o;
			}
			else
				return null;
		}
		catch (IOException ex)
		{
			return null;
		}
		catch (ClassNotFoundException ex)
		{
			return null;
		}
	}
	
	
	
	/**
	 * @return uchwyt polaczenia z baza, ktory jest potrzebny, zeby wysylac swoje
	 * zapytania SQL do bazy
	 */
	public Connection pobierz_uchwyt_polaczenia()
	{
		return conn;
	}
	
	/*
	public static void main(String[] args){
		
		DziennikMain.polaczenie = new Polaczenie();
		DziennikMain.polaczenie.polacz_z_baza();
		DziennikMain.connection = DziennikMain.polaczenie.pobierz_uchwyt_polaczenia();
		
		ArrayList<Przedmiot> prz = new ArrayList<Przedmiot>();
		ArrayList<Ocena> oc1 = new ArrayList<Ocena>();
		ArrayList<Ocena> oc2 = new ArrayList<Ocena>();
		ArrayList<Ocena> oc3 = new ArrayList<Ocena>();
		oc1.add(new Ocena("pierwsza ocena1", 5.5, 1));
		oc1.add(new Ocena("druga ocena1", 4.0, 1));
		oc1.add(new Ocena("trzecia ocena1", 5.0, 1));
		oc2.add(new Ocena("pierwsza ocena2", 3.5, 1));
		oc2.add(new Ocena("druga ocena2", 4.5, 1));
		oc3.add(new Ocena("pierwsza ocena3", 5.5, 1));
		
		ArrayList<Obecnosc> obec1 = new ArrayList<Obecnosc>();
		ArrayList<Obecnosc> obec2 = new ArrayList<Obecnosc>();
		
		
		obec1.add(new Obecnosc("2012-01-01", 'o'));
		obec1.add(new Obecnosc("2012-01-02", 'o'));
		obec1.add(new Obecnosc("2012-01-03", 's'));
		obec1.add(new Obecnosc("2012-01-04", '-'));
		obec2.add(new Obecnosc("2012-01-04", 'o'));
		obec2.add(new Obecnosc("2012-01-05", '-'));
		
		
		prz.add(new Przedmiot(oc1,obec1,"Jezyk polski"));
		prz.add(new Przedmiot(oc2,obec2,"Matematyka"));
		prz.add(new Przedmiot(oc3,null,"Fizyka"));
		prz.add(new Przedmiot(null,null,"Chemia"));
		prz.add(new Przedmiot(null,null,"Technika"));
		prz.add(new Przedmiot(null,null,"Biologia"));
		prz.add(new Przedmiot(null,null,"Geografia"));
		prz.add(new Przedmiot(null,null,"Wiedza o spolecze�stwie"));
		prz.add(new Przedmiot(null,null,"Historia"));
		prz.add(new Przedmiot(null,null,"Informatyka"));
		prz.add(new Przedmiot(null,null,"Sztuka"));
		prz.add(new Przedmiot(null,null,"Wychowanie do z.w.r."));
		prz.add(new Przedmiot(null,null,"Wychowanie fizyczne"));
		prz.add(new Przedmiot(null,null,"Jezyk Angielski"));
		prz.add(new Przedmiot(null,null,"Jezyk Niemiecki"));
		prz.add(new Przedmiot(null,null,"Religia"));
		
		ArrayList<Uwaga> uw = new ArrayList<Uwaga>();
		
		uw.add(new Uwaga("Ucze� rzucal miesem.","2012-02-02",12,false,"Matematyka"));
		uw.add(new Uwaga("Ucze� obnazal sie przed klasa i nauczycielem.","2012-02-03",12,false,"Jezyk Polski"));

		Uczen_klasy uk = new Uczen_klasy(99999, prz, uw);
		
		DziennikMain.polaczenie.dodaj_ucznia(uk, "dziennik_3a_20112012");
	}
	*/
	
	// przykladowy main, patrzec i uczyc sie
	/*public static void main(String[] args)
	{
		Polaczenie p = new Polaczenie();
		p.polacz_z_baza();
		
		Connection c = p.pobierz_uchwyt_polaczenia();
		
		try
		{
			Statement st = c.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
			
			// pobieranie danych konkretnego ucznia z dziennika w bazie i deserializowanie go
			// (na tej samej zasadzie, co deserializacja przedmiotow i uwag, dziala tez deserializowanie uprawnien nauczycieli)
			
				// najpierw pobieramy wiersze z bazy
				ResultSet rs = st.executeQuery("select * from dziennik_3a_20112012");
				
				// przechodzimy przez wszystkie wiersze zwroconego wyniku w poszukiwaniu pozadanego ucznia
				while (rs.next())
	            {
					// zapisujemy id ucznia do zmiennej num, pobierajac ja z kolumny "id" w bazie
					int num = rs.getInt("id");
					
					// jesli znalezlismy pozadanego ucznia, to
					if (num == 153333337)
					{
						// zapisujemy zdeserializowany obiekt typu BLOB zawierajacy tablice przedmiotow
						ArrayList<Przedmiot> prz = p.deserializuj(rs.getBytes("przedmioty"));
						// i zdeserializowany obiekt BLOB, zawierajacy tablice uwag dla danego ucznia
						ArrayList<Uwaga> uw = p.deserializuj(rs.getBytes("uwagi"));
						
						if (prz == null || uw == null)
						{
							System.out.println("pusta lista przedmiotow");
						}
						else
						{
							System.out.println(prz.get(0).nazwa);
							System.out.println(uw.get(0).tresc);
						}
							
						// tworzymy nowego ucznia w programie i przypisujemy mu zdeserializowane wartosci
						Uczen_klasy uk_nowy = new Uczen_klasy(num);
						uk_nowy.przedmioty = prz;
						uk_nowy.uwagi = uw;
						
							// zmiana danych tego ucznia i serializacja ich do bazy
								uk_nowy.przedmioty.get(0).nazwa = "nazwa zmieniona lazco�z";
								uk_nowy.uwagi.add(new Uwaga("nowa uwaga", "1666-12-24", 3, true, "fizyka"));
								
								// przenosimy kursor na poczatek zbioru wynikow (wierszy), zeby znow je wszystkie przeszukac
								rs.beforeFirst();
								
								while (rs.next())
								{
									// zapisujemy id ucznia do zmiennej num, pobierajac ja z kolumny "id" w bazie
									int num2 = rs.getInt("id");
									
									// jesli znalezlismy pozadanego ucznia, to
									if (num2 == uk_nowy.id)
									{
										rs.updateObject("przedmioty", p.serializuj(uk_nowy.przedmioty));
										rs.updateObject("uwagi", p.serializuj(uk_nowy.uwagi));
										rs.updateRow();
									}
								}
							// ---
					}
	            }
			// ---
		}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
		
		// tworzenie nowego ucznia i dodawanie go do bazy
			Uczen_klasy uk = new Uczen_klasy(153333337);
			
			// tworzymy nowa klase Uwaga
			Uwaga u = new Uwaga("bo nie lubie poniedzialkow", "2011-12-30", 15, false, "wiedza o zuciu");
			
			// tworzymy nowa klase Przedmiot
			Przedmiot prz = new Przedmiot(new ArrayList<Ocena>(), new ArrayList<Obecnosc>(), "nowy przedmiot");
			prz.obecnosc.add(new Obecnosc("2011-10-10", '-'));
			prz.ocena.add(new Ocena("kartkowka ze slowek", 5.5, 1));
			
			// dodajemy do list w klasie Uczen_klasy utworzone przed chwila dwa obiekty
			uk.przedmioty.add(prz);
			uk.uwagi.add(u);

			// dodajemy nowo stworzonego ucznia do bazy do dziennika "dziennik_3a_20112012"
			p.dodaj_ucznia(uk, "dziennik_3a_20112012");
		// ---
		
		p.rozlacz_z_baza();
	}*/
	
	
	
	/**
	 * @return uchwyt statement z klasy Polaczenie
	 */
	public Statement getStatement()
	{
		if (st != null)
			return st;
		else
			return null;
	}
	
	
	
	/**
	 * uproszczone wysylanie zapyta� typu select do bazy
	 * @param zapytanie - la�cuch znakow zawierajacy zapytanie, np. "select * from dziennik_3a_20112012"
	 * @return zbior wynikow zwrocony przez baze. Uwaga! Zbior moze byc pusty, jesli zapytanie zwroci
	 * pusty wynik. W innym przypadku jest to po prostu zbior rekordow pobranych z bazy.
	 */
        
    public static ResultSet MySQL_zapytanie (String zapytanie)
    {
        Polaczenie polaczenie_bean;
        // DKA: pobieranie obiektu ziarna z application scope (w tym wypadku pobranie polaczenia)
                FacesContext facesContext = FacesContext.getCurrentInstance();
                Application application = facesContext.getApplication();
                ValueBinding binding = application.createValueBinding("#{polaczenie}");
                polaczenie_bean = (Polaczenie)binding.getValue(facesContext);
                System.out.println("User Name : "+polaczenie_bean.getUserName());

    	ResultSet rs=null;
    	try
		{
			Statement st = polaczenie_bean.getConn().createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
			rs = st.executeQuery(zapytanie);
			}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
    	return rs;
    }
 
	
	
}



